#pragma once

#include <vector>
#include <memory>
#include "SRGLinc.h"
#include "SRMacro.h"
#include "SRData.h"
#include "SRType.h"

namespace sora
{
	class DrawCommand;
	class Model;
	class BoundingBox;
	class MeshData;
	class WireFrameData;
	class MeshModel;
	class Vertex3;
	class SimpleVertex3;
	class ModelVertex3;

	enum ModelType {
		ModelTypeMesh,
		ModelTypeWireframe,
		ModelTypeBoundingBox,
		ModelTypePrimitive,
		ModelTypeWireFrameData,
		ModelTypeMeshData,
	};

	class Model {
	public:
		Model() {}
		virtual ~Model() {}
		virtual ModelType type() const = 0;
		virtual const std::vector<DrawCommand> getDrawCommand() const = 0;
	};

	class MeshModel : public Model {
	public:
		MeshModel(const std::vector<MeshData*> dataList);
		virtual ~MeshModel();
		ModelType type() const;
		const std::vector<DrawCommand> getDrawCommand() const;

	private:
		std::vector<MeshData*> dataList_;

		friend class WireFrameModel;
	};

	class WireFrameModel : public Model {
	public:
		WireFrameModel(const MeshModel &mesh);
		virtual ~WireFrameModel();
		ModelType type() const;
		const std::vector<DrawCommand> getDrawCommand() const;

	private:
		std::vector<WireFrameData*> dataList_;
	};

	class MeshData : public Model {
	public:
		MeshData(GLenum mode, const std::vector<ModelVertex3> &vertexArray,
				 const std::vector<GLushort> &indexArray,
				 bool useTex, bool useNormal);
		ModelType type() const;
		bool useTex() const;
		bool useNormal() const;
		const std::vector<DrawCommand> getDrawCommand() const;

	private:
		GLenum mode() const;
		const std::vector<ModelVertex3> &vertexArray() const;
		const std::vector<GLushort> &indexArray() const;

		const GLenum mode_;
		std::vector<ModelVertex3> vertexArray_;
		std::vector<GLushort> indexArray_;
		const bool useTex_;
		const bool useNormal_;

		friend class WireFrameData;
		friend class MeshModel;
	};

	class WireFrameData : public Model {
	public:
		WireFrameData(const MeshData &mesh);
		ModelType type() const;

		GLenum mode() const;
		const std::vector<DrawCommand> getDrawCommand() const;
		
	private:
		const std::vector<SimpleVertex3> &vertexList() const;
		const std::vector<GLushort> &indexList() const;

		const GLenum mode_;
		std::vector<SimpleVertex3> vertexList_;
		std::vector<GLushort> indexList_;
	};

	class BoundingBox : public Model {
	public:
		BoundingBox(const Box &box);
		BoundingBox(const BoundingBox &box);
		BoundingBox(float top, float bottom, float left, float right, float front, float back);
		BoundingBox(Model *model);
		BoundingBox(ModelPtr model);

		ModelType type() const;

		float width() const;
		float height() const;
		float depth() const;

		const std::vector<DrawCommand> getDrawCommand() const;
	private:
		void init();
		Box model2box(Model *model);
		std::auto_ptr<Box> box_;

		std::vector<SimpleVertex3> vertexList_;
		std::vector<GLushort> indexList_;
	};

#if _IPHONE_
#pragma mark builder
#endif
	class MeshModelBuilder {
	public:
		void addMesh(GLenum mode, const std::vector<ModelVertex3> &vertexList,
					 const std::vector<GLushort> &indexList,
					 bool useTex, bool useNormal);
		Model* build();
	private:
		std::vector<MeshData*> dataList_;
	};
}
